import os
import re
import urllib.parse
import ipaddress
from datetime import datetime
from src.core.database import DatabaseConnector
from src.models.asset import Asset
from src.models.ip import IPAddress
from src.models.domain import Domain
from src.io.file_exporter import FileExporter
from src.io.csv_importer import CsvImporter
from src.core.help_generator import HelpGenerator
from src.core.dbhelper import DBHelper
from src.core.utils import *
from src.core.service_scanner import ServiceScanner

class AssetCommand:
    """资产命令处理类"""
    
    def __init__(self, db_connector=None, subparsers=None):
        """初始化资产命令处理器"""
        self.db_connector = db_connector or DatabaseConnector()
        self.csv_importer = CsvImporter(self.db_connector)
        self.db_helper = DBHelper(self.db_connector)
        self.arg_parser = self.setup_parser(subparsers)
    
    def setup_parser(self, subparsers):
        """设置资产管理命令行参数解析器"""
        # asset命令组
        asset_parser = subparsers.add_parser('asset', help='资产管理相关命令', description='资产管理相关的所有操作')
        asset_subparsers = asset_parser.add_subparsers(dest='asset_command', help='资产管理子命令')
        
        # asset help命令
        asset_help_parser = asset_subparsers.add_parser('help', help='显示资产管理命令帮助', description='显示资产管理命令的详细帮助信息')
        
        # asset add命令
        asset_add_parser = asset_subparsers.add_parser('add', help='添加资产', description='添加一条新的资产记录到系统中')
        asset_add_parser.add_argument('--name', help='资产名称')
        asset_add_parser.add_argument('--ip', help='IP地址')
        asset_add_parser.add_argument('--domain', help='域名')
        asset_add_parser.add_argument('--port', required=True, help='端口号', type=int)
        asset_add_parser.add_argument('--protocol', choices=['tcp', 'udp'], help='协议', default='tcp')
        asset_add_parser.add_argument('--tags', help='标签')
        asset_add_parser.add_argument('--owner', help='所属单位名称')
        
        # asset set命令
        asset_set_parser = asset_subparsers.add_parser('set', help='更新资产信息', description='修改指定资产的各项属性信息')
        asset_set_parser.add_argument('identifier', help='资产ID或名称')
        asset_set_parser.add_argument('--name', help='新的资产名称')
        asset_set_parser.add_argument('--ip', help='IP地址')
        asset_set_parser.add_argument('--domain', help='域名')
        asset_set_parser.add_argument('--port', help='端口号', type=int)
        asset_set_parser.add_argument('--protocol', help='协议', choices=['tcp', 'udp'])
        asset_set_parser.add_argument('--tags', help='标签')
        asset_set_parser.add_argument('--owner', help='所属单位名称')
        
        # asset delete命令
        asset_delete_parser = asset_subparsers.add_parser('delete', help='删除资产', description='从系统中删除指定的资产记录')
        asset_delete_parser.add_argument('identifier', help='资产ID或名称')
        
        # asset get命令
        asset_get_parser = asset_subparsers.add_parser('get', help='获取资产信息', description='查询并显示指定资产的详细信息')
        asset_get_parser.add_argument('identifier', help='资产ID或名称')
        
        # asset getall命令
        asset_getall_parser = asset_subparsers.add_parser('getall', help='获取所有资产列表', description='查询并显示系统中的所有资产信息')
        # 设置--export为可选参数，不提供参数值时自动生成文件名
        asset_getall_parser.add_argument('-o', '--output', nargs='?', const=True, help='导出文件路径，不指定则自动生成')
        asset_getall_parser.add_argument('-e', '--extension', choices=['json', 'csv'], default='csv', help='导出文件格式，默认csv')
        
        # asset search命令
        asset_search_parser = asset_subparsers.add_parser('search', help='搜索资产', description='根据关键词搜索匹配的资产信息')
        asset_search_parser.add_argument('keyword', nargs='?', help='搜索关键词')
        asset_search_parser.add_argument('--fingerprint', help='搜索指纹')
        asset_search_parser.add_argument('--title', help='搜索标题')
        asset_search_parser.add_argument('--owner', help='搜索所属单位')
        asset_search_parser.add_argument('--ip', help='搜索IP地址')
        asset_search_parser.add_argument('--domain', help='搜索域名')
        asset_search_parser.add_argument('--port', help='搜索端口')
        asset_search_parser.add_argument('--protocol', help='搜索协议', choices=['tcp', 'udp'])
        asset_search_parser.add_argument('--tag', help='搜索标签')
        asset_search_parser.add_argument('--name', help='搜索名称')
        # 设置--export为可选参数，不提供参数值时自动生成文件名
        asset_search_parser.add_argument('-o', '--output', nargs='?', const=True, help='导出文件路径，不指定则自动生成')
        asset_search_parser.add_argument('-e', '--extension', choices=['json', 'csv'], default='csv', help='导出文件格式，默认csv')
        asset_search_parser.add_argument('-s', '--scan', help='服务探测搜索到的资产', action='store_true')
        asset_search_parser.add_argument('-f', '--scan-filter', help='过滤条件，仅对探测到的资产进行过滤', choices=['all', 'invalid'], default='invalid')
        asset_search_parser.add_argument('-w', '--scan-timeout', default=2, help='扫描超时时间，默认为2秒')
        asset_search_parser.add_argument('--scan-retry', default=1, help='扫描失败后重试次数，默认为1次')
        asset_search_parser.add_argument('--scan-threads', default=100, help='每个IP的扫描线程数，默认为100个线程')
        # 联网检测，避免网络中断所有探测无效
        asset_search_parser.add_argument('--network-check-ip', default='8.8.8.8', help='设置联网检测的测试IP地址，避免进行无效探测')
        
        # asset scan命令
        asset_scan_parser = asset_subparsers.add_parser('scan', help='资产探测', description='对指定资产进行服务探测')
        asset_scan_parser.add_argument('identifier', help='资产ID或URL')
        asset_scan_parser.add_argument('-w', '--scan-timeout', default=2, help='扫描超时时间，默认为2秒')
        asset_scan_parser.add_argument('--scan-retry', default=1, help='扫描失败后重试次数，默认为1次')
        asset_scan_parser.add_argument('--scan-threads', default=100, help='每个IP的扫描线程数，默认为100个线程')
        # 联网检测，避免网络中断所有探测无效
        asset_scan_parser.add_argument('--network-check-ip', default='8.8.8.8', help='设置联网检测的测试IP地址，避免进行无效探测')

        # asset import命令
        asset_import_parser = asset_subparsers.add_parser('import', help='从CSV文件导入资产', description='从CSV格式文件批量导入资产数据')
        asset_import_parser.add_argument('file_path', help='CSV文件路径')
        asset_import_parser.add_argument('--ip-column', help='IP列的索引或名称，默认为列名称为"ip"的列', default='ip')
        asset_import_parser.add_argument('--domain-column', help='域名列的索引或名称，默认为列名称为"domain"的列', default='domain')
        asset_import_parser.add_argument('--name-column', help='名称列的索引或名称，默认为列名称为"name"的列', default='name')
        asset_import_parser.add_argument('--port-column', help='端口列的索引或名称，默认为列名称为"port"的列', default='port')
        asset_import_parser.add_argument('--protocol-column', help='协议列的索引或名称，默认为列名称为"protocol"的列', default='protocol')
        asset_import_parser.add_argument('--fingerprint-column', help='指纹列的索引或名称，默认为列名称为"fingerprint"的列', default='fingerprint')
        asset_import_parser.add_argument('--title-column', help='标题列的索引或名称，默认为列名称为"title"的列', default='title')
        asset_import_parser.add_argument('--owner-column', help='所属单位列的索引或名称，默认为列名称为"owner"的列', default='owner')

        return asset_parser
       
    def handle_command(self, args):
        command = args.asset_command
        if command == 'help':
            self.show_asset_help()
        elif command == 'add':
            # 处理asset add命令
            name = args.name
            ip = args.ip
            domain = args.domain
            port = args.port
            protocol = args.protocol
            tags = args.tags or ''
            self.add_asset_new(name=name, ip=ip, domain=domain, port=str(port), protocol=protocol, tags=tags, owner=args.owner)
        elif command == 'set':
            # 处理asset set命令
            identifier = args.identifier
            
            # 收集所有设置的字段
            fields = {}
            if hasattr(args, 'name') and args.name is not None:
                fields['asset_name'] = args.name
            if hasattr(args, 'ip') and args.ip is not None:
                fields['ip'] = args.ip
            if hasattr(args, 'domain') and args.domain is not None:
                fields['domain'] = args.domain
            if hasattr(args, 'port') and args.port is not None:
                fields['port'] = args.port
            if hasattr(args, 'protocol') and args.protocol is not None:
                fields['protocol'] = args.protocol
            if hasattr(args, 'tags') and args.tags is not None:
                fields['tags'] = args.tags
            if hasattr(args, 'owner') and args.owner is not None:
                fields['owner'] = args.owner
            
            # 调用asset_command的set_asset方法
            for field, value in fields.items():
                self.set_asset(identifier, field, value)
        elif command == 'delete':
            # 处理asset delete命令
            self.delete_asset(args.identifier)
        elif command == 'get':
            # 处理asset get命令
            self.get_asset(args.identifier)
        elif command == 'getall':
            # 处理asset getall命令
            # 获取导出相关参数
            export_path = args.output
            extension = args.extension
            self.get_all_assets(export_path=export_path, extension=extension)
        elif command == 'search':
            # 处理asset search命令
            keyword = args.keyword
            filters = {
                'name': args.name,
                'ip': args.ip,
                'domain': args.domain,
                'port': args.port,
                'protocol': args.protocol,
                'tag': args.tag,
                'owner': args.owner,
                'fingerprint': args.fingerprint,
                'title': args.title
            }
            export_path = args.output
            extension = args.extension
            self.search_assets(keyword, filters, export_path, extension, args.scan, args.scan_filter)
        elif command == 'scan':
            # 处理asset scan命令
            self.scan_asset(args.identifier)
        elif command == 'import':
            # 处理asset import命令
            file_path = args.file_path
            self.import_assets(file_path, 'asset')
        else:
            print(f"未知的资产命令: {command}")
            self.show_asset_help()
        
    
    def show_asset_help(self):
        """显示资产管理命令帮助信息"""
        self.arg_parser.print_help()
        return
        # 使用帮助生成器动态生成帮助信息
        help_text = HelpGenerator.generate_asset_help(self.arg_parser)
        print(help_text)
    
    def add_asset_new(self, name=None, ip=None, domain=None, port=None, protocol=None, tags=None, owner_identifier=None):
        current_time = datetime.now()
        
        # 验证必需参数
        if not port or not protocol:
            print("错误：必须指定端口和协议")
            return
        
        if not ip and not domain:
            print("错误：必须指定IP或域名中的至少一个")
            return
        
        # 检查端口是否为整数
        if not port.isdigit():
            print("错误：端口必须是整数")
            return
        port = int(port)
        # port范围检查
        if port < 1 or port > 65535:
            print("错误：端口必须在1到65535之间")
            return

        # 检查协议是否有效
        if protocol not in ['tcp', 'udp']:
            print("错误：协议必须是tcp或udp")
            return

        # 检查IP地址是否有效
        if ip:
            try:
                ipaddress.ip_address(ip)
            except ValueError:
                print("错误：无效的IP地址")
                return

        # 检查资产是否已存在（通过IP、域名、端口、协议的组合）
        item = self.scan_asset_exist(domain, ip, port, protocol)
        if item:
            print(f"错误：资产已存在（IP={ip}, 域名={domain}, 端口={port}, 协议={protocol}），冲突资产ID: {item['asset_id']}")
            return

        # 检查域名是否有效
        if not self.db_helper.is_valid_domain(domain):
            print("错误：无效的域名")
            return

        owner_id = None
        owner_name = None
        if owner_identifier:
            try:
                owner = self.db_helper.get_or_add_owner(owner_identifier)
                if owner:
                    owner_id = owner['owner_id']
                    owner_name = owner['owner_name']
            except Exception as e:
                print(f"获取或添加所有者失败: {e}")
                return

        
        # 添加资产，使用正确的表列名
        sql = "INSERT INTO assets (asset_name, ip, port, domain, asset_protocol, asset_tags, owner_id, owner_name, create_time, update_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"
        
        try:
            cursor = self.db_connector.execute_query(sql, (name, ip, port, domain, protocol, tags, owner_id, owner_name, current_time, current_time))
            
            if cursor:
                print(f"成功添加资产: ID={cursor.lastrowid if hasattr(cursor, 'lastrowid') else '未知'}")
                if name:
                    print(f"  名称: {name}")
                if ip:
                    print(f"  IP: {ip}")
                if domain:
                    print(f"  域名: {domain}")
                print(f"  端口: {port}")
                print(f"  协议: {protocol}")
                if tags:
                    print(f"  标签: {tags}")
                # if owner_info:
                #     print(f"  所属单位: {owner_info['owner_name']} (ID: {owner_info['owner_id']})" if isinstance(owner_info, dict) else f"  所属单位: {owner_info}")
        except Exception as e:
            print(f"添加资产失败: {e}")
    
    def scan_asset_exist(self, domain, ip, port, protocol):
        sql = "SELECT * FROM assets WHERE port = %s AND asset_protocol = %s"
        params = [port, protocol]
        if ip:
            sql += " AND ip = %s"
            params.append(ip)
        if domain:
            sql += " AND domain = %s"
            params.append(domain)
        item = self.db_connector.execute_query(sql, params).fetchone()
        return item
        

    def set_asset(self, asset_id, field, value):
        """修改资产信息"""
        # 验证字段名
        valid_fields = ['asset_name', 'ip', 'domain', 'port', 'protocol', 'tags', 'owner']
        if field not in valid_fields:
            print(f"错误: 无效的字段名 {field}！可用字段: {', '.join(valid_fields)}")
            return
        
        # 检查资产是否存在
        sql = "SELECT * FROM assets WHERE asset_id = %s"
        asset_result = self.db_connector.execute_query(sql, (asset_id,)).fetchone()
        if not asset_result:
            print(f"错误: 资产ID {asset_id} 不存在！")
            return

        # 检查更新后是否与现有资产有冲突（通过IP、域名、端口、协议的组合）
        fields = {'ip': asset_result['ip'], 'domain': asset_result['domain'], 'port': asset_result['port'], 'protocol': asset_result['asset_protocol']}
        if field in fields.keys():
            fields[field] = value
            item = self.scan_asset_exist(fields['domain'], fields['ip'], fields['port'], fields['protocol'])
            if item:
                if item['asset_id'] != asset_id:
                    print(f"错误：若更新资产冲突（IP={fields['ip']}, 域名={fields['domain']}, 端口={fields['port']}, 协议={fields['protocol']}），冲突资产ID: {item['asset_id']}")
                    return

        # 更新资产信息
        now = datetime.now()
        fields = {
            'name': 'asset_name',
            'ip': 'ip',
            'domain': 'domain',
            'port': 'port',
            'protocol': 'asset_protocol',
            'tags': 'asset_tags',
            'owner': 'owner_name'
        }
        sql = f"UPDATE assets SET {fields[field]} = %s, update_time = %s WHERE asset_id = %s"
        
        try:
            self.db_connector.execute_query(sql, (value, now, asset_id))
            print(f"成功更新资产 {asset_id} 的 {field} 字段为 {value}")
        except Exception as e:
            print(f"更新资产失败: {e}")
    
    def delete_asset(self, asset_id):
        """删除资产"""
        # 检查资产是否存在
        sql = "SELECT * FROM assets WHERE asset_id = %s"
        asset_result = self.db_connector.execute_query(sql, (asset_id,))
        if not asset_result:
            print(f"错误: 资产ID {asset_id} 不存在！")
            return
        
        # 删除资产
        sql = "DELETE FROM assets WHERE asset_id = %s"
        
        try:
            self.db_connector.execute_query(sql, (asset_id,))
            print(f"成功删除资产 {asset_id}")
        except Exception as e:
            print(f"删除资产失败: {e}")
    
    def get_asset(self, asset_id):
        """获取指定资产信息"""
        # 查询资产信息
        sql = "SELECT a.*, o.owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id WHERE asset_id = %s"
        cursor = self.db_connector.execute_query(sql, (asset_id,))
        
        if not cursor:
            print(f"错误: 资产ID {asset_id} 不存在！")
            return
        
        # 获取结果
        result = cursor.fetchone()
        if not result:
            print(f"错误: 资产ID {asset_id} 不存在！")
            return
        
        json_head = [
            { 'name': '资产ID', 'key': 'asset_id' },
            { 'name': '资产名称', 'key': 'asset_name' },
            { 'name': '域名', 'key': 'domain' },
            { 'name': 'IP', 'key': 'ip' },
            { 'name': '端口', 'key': 'port' },
            { 'name': '协议', 'key': 'asset_protocol' },
            { 'name': '指纹', 'key': 'asset_fingerprint' },
            { 'name': '标签', 'key': 'asset_tags' },
            { 'name': '标题', 'key': 'asset_title' },
            { 'name': '所属单位', 'key': 'owner_name' },
            { 'name': '更新时间', 'key': 'update_time' },
            { 'name': '创建时间', 'key': 'create_time' },
            { 'name': '响应内容', 'key': 'asset_response' }
        ]
        show_entity("资产信息", json_head, result)
    
    def get_all_assets(self, export_path=None, extension=None):
        """获取所有资产信息，支持导出到文件"""
        # 查询所有资产信息
        sql = "SELECT a.*, o.owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id ORDER BY a.create_time DESC"
        cursor = self.db_connector.execute_query(sql)
        
        if not cursor:
            print("暂无资产信息！")
            return
        
        # 获取结果列表
        assets_result = cursor.fetchall()
        
        if not assets_result:
            print("暂无资产信息！")
            return
        
        # 准备导出数据
        export_data = []
        for asset in assets_result:
            # 转换datetime对象为字符串
            export_item = asset.copy()
            if 'create_time' in export_item and isinstance(export_item['create_time'], datetime):
                export_item['create_time'] = export_item['create_time'].strftime('%Y-%m-%d %H:%M:%S')
            if 'update_time' in export_item and isinstance(export_item['update_time'], datetime):
                export_item['update_time'] = export_item['update_time'].strftime('%Y-%m-%d %H:%M:%S')
            export_data.append(export_item)
        
        # 导出数据
        if export_path:
            # 如果export_path为True，表示需要自动生成文件名
            actual_path = None if export_path is True else export_path
            self.file_exporter.export_data(export_data, actual_path, 'all_assets', extension)
            return
        
        # 显示资产列表
        json_head = [
            { 'name': 'ID', 'key': 'asset_id', 'width': 8 },
            { 'name': '名称', 'key': 'asset_name', 'width': 20, 'align': 'center' },
            { 'name': '所属单位', 'key': 'owner_name', 'width': 20 },
            { 'name': '域名', 'key': 'domain', 'width': 20, 'align': 'center' },
            { 'name': 'IP', 'key': 'ip', 'width': 15 },
            { 'name': '端口', 'key': 'port', 'width': 9, 'render': lambda item : f"{str(item['port'])}/{str(item['asset_protocol'])}" },
            { 'name': '是否有效', 'key': 'is_valid', 'width': 8 },
            { 'name': '指纹', 'key': 'asset_fingerprint', 'width': 20 },
            { 'name': '状态码', 'key': 'status_code', 'width': 10 },
            { 'name': '标题', 'key': 'asset_title', 'width': 30 },
            { 'name': '最后检测时间', 'key': 'last_check_time', 'width': 19 },
            #{ 'name': '标签', 'key': 'asset_tags', 'width': 20 },
        ]
        show_table("资产列表", json_head, assets_result)
    
    def search_assets(self, keyword, filters=None, export_path=None, extension=None, scan=False, scan_filter='invalid'):
        """搜索资产"""
        # 检查是否为URL格式，如果是则解析域名和端口
        test_url = False
        parsed_domain = None
        parsed_port = None
        parsed_ip = None
        
        # 只有当关键字中包含冒号时才尝试解析URL
        if keyword and ':' in keyword:
            test_url = True
            # 尝试解析URL
            temp = keyword
            if keyword.startswith(('http://')):
                temp = keyword[7:]
                parsed_port = 80
            elif keyword.startswith(('https://')):
                temp = keyword[8:]
                parsed_port = 443
            temp = temp.split('/', 1)[0]
            if ':' in temp:
                domain, port = temp.split(':', 1)
                try:
                    parsed_port = int(port)
                    temp = domain
                except ValueError:
                    pass  # 端口不是数字，忽略
            # 简单通过有没有字母判断是否为IP地址
            try:
                ipaddress.ip_address(temp)
                parsed_ip = temp
            except ValueError:
                parsed_domain = temp
        
        # 构建sql语句
        sql = "SELECT a.*, o.owner_name as relation_owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id WHERE 1=1"
        params = []
        # 添加搜索条件
        if test_url:
            if parsed_domain:
                sql += " AND domain = %s"
                params.append(parsed_domain)
            if parsed_port:
                sql += " AND port = %s"
                params.append(parsed_port)
            if parsed_ip:
                sql += " AND ip = %s"
                params.append(parsed_ip)
        elif filters:
            if 'port' in filters and filters['port']:
                # 端口是数字，直接精确匹配
                try:
                    port_num = int(filters['port'])
                    sql += " AND port = %s"
                    params.append(port_num)
                except ValueError:
                    print(f"错误：搜索端口时，关键字 '{filters['port']}' 必须是数字")
                    return
            if 'ip' in filters and filters['ip']:
                # IP地址使用精确匹配
                sql += " AND ip LIKE %s"
                params.append(f"%{filters['ip']}%")
            if 'domain' in filters and filters['domain']:
                # 域名使用模糊匹配
                sql += " AND domain LIKE %s"
                params.append(f"%{filters['domain']}%")
            if 'name' in filters and filters['name']:
                # 资产名称使用模糊匹配
                sql += " AND asset_name LIKE %s"
                params.append(f"%{filters['name']}%")
            if 'protocol' in filters and filters['protocol']:
                sql += " AND asset_protocol = %s"
                params.append(filters['protocol'])
            if 'tag' in filters and filters['tag']:
                sql += " AND asset_tags LIKE %s"
                params.append(f"%{filters['tag']}%")
            if 'owner' in filters and filters['owner']:
                sql += " AND (o.owner_name LIKE %s OR a.owner_name LIKE %s)"
                params.append(f"%{filters['owner']}%")
                params.append(f"%{filters['owner']}%")
            if 'fingerprint' in filters and filters['fingerprint']:
                sql += " AND asset_fingerprint LIKE %s"
                params.append(f"%{filters['fingerprint']}%")
            if 'title' in filters and filters['title']:
                sql += " AND asset_title LIKE %s"
                params.append(f"%{filters['title']}%")
            if 'response' in filters and filters['response']:
                sql += " AND asset_response LIKE %s"
                params.append(f"%{filters['response']}%")

            if keyword:
                # 搜索其它字段
                sql += " AND (1=0"
                if 'name' not in filters or not filters['name']:
                    sql += " OR asset_name LIKE %s"
                    params.append(f"%{keyword}%")
                if 'ip' not in filters or not filters['ip']:
                    sql += " OR ip LIKE %s"
                    params.append(f"%{keyword}%")
                if 'domain' not in filters or not filters['domain']:
                    sql += " OR domain LIKE %s"
                    params.append(f"%{keyword}%")
                if 'port' not in filters or not filters['port']:
                    try:
                        port_num = int(keyword)
                        sql += " OR port = %s"
                        params.append(port_num)
                    except ValueError:
                        pass  # 关键字不是数字，忽略
                if 'protocol' not in filters or not filters['protocol']:
                    sql += " OR asset_protocol = %s"
                    params.append(keyword)
                # sql += " OR asset_tags LIKE %s"
                # params.append(f"%{keyword}%")
                # sql += " OR o.owner_name LIKE %s"
                # params.append(f"%{keyword}%")
                # sql += " OR asset_fingerprint LIKE %s"
                # params.append(f"%{keyword}%")
                # sql += " OR asset_title LIKE %s"
                # params.append(f"%{keyword}%")
                # sql += " OR asset_response LIKE %s"
                # params.append(f"%{keyword}%")
                sql += ")"
        
        # 如果使用了--scan参数，则允许没有搜索条件
        if scan and len(params) == 0:
            # 使用--scan参数时没有搜索条件，将获取所有资产
            sql = "SELECT a.*, o.owner_name as relation_owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id"
            params = []
        elif len(params) == 0:
            print("没有提供搜索条件！")
            return
        assets_result = self.db_connector.fetchall(sql, params)
        if len(assets_result) == 0:
            print(f"未找到资产！")
            return

        if scan:
            self._scan_assets(assets_result, scan_filter)
            return
        
        # 准备导出数据
        export_data = []
        for asset in assets_result:
            # 转换datetime对象为字符串
            export_item = asset.copy()
            if 'create_time' in export_item and isinstance(export_item['create_time'], datetime):
                export_item['create_time'] = export_item['create_time'].strftime('%Y-%m-%d %H:%M:%S')
            if 'update_time' in export_item and isinstance(export_item['update_time'], datetime):
                export_item['update_time'] = export_item['update_time'].strftime('%Y-%m-%d %H:%M:%S')
            export_data.append(export_item)
        
        # 导出数据
        if export_path:
            actual_path = None if export_path is True else export_path
            self.file_exporter.export_data(export_data, actual_path, 'asset_search', extension)
            return
        
        # 显示搜索结果
        json_head = [
            { 'name': 'ID', 'key': 'asset_id', 'width': 8 },
            { 'name': '名称', 'key': 'asset_name', 'width': 20, 'align': 'center' },
            { 'name': '所属单位', 'key': 'owner_name', 'width': 20 },
            { 'name': '域名', 'key': 'domain', 'width': 20, 'align': 'center' },
            { 'name': 'IP', 'key': 'ip', 'width': 15 },
            { 'name': '端口', 'key': 'port', 'width': 9, 'render': lambda item : f"{str(item['port'])}/{str(item['asset_protocol'])}" },
            { 'name': '是否有效', 'key': 'is_valid', 'width': 8, 'dict': { 0: '无效', 1: '有效' } },
            { 'name': '指纹', 'key': 'asset_fingerprint', 'width': 20 },
            { 'name': '状态码', 'key': 'status_code', 'width': 6 },
            { 'name': '标题', 'key': 'asset_title', 'width': 30 },
            { 'name': '最后检测时间', 'key': 'last_check_time', 'width': 19 },
        ]
        show_table("资产搜索结果", json_head, assets_result)

    
    def import_assets(self, file_path, name_column, domain_column, ip_column, port_column, protocol_column, owner_column, fingerprint_column):
        """从文件导入资产"""
        # 验证文件是否存在
        if not os.path.exists(file_path):
            print(f"错误: 文件 {file_path} 不存在！")
            return
        
        # 检查文件扩展名
        file_ext = os.path.splitext(file_path)[1].lower()
        if file_ext != '.csv':
            print(f"错误: 只支持CSV文件格式，当前文件格式为 '{file_ext}'")
            return
        
        try:
            success_count, failed_count = self.csv_importer.import_assets_from_csv(
                file_path=file_path, 
                name_column=name_column, 
                domain_column=domain_column, 
                ip_column=ip_column, 
                port_column=port_column, 
                protocol_column=protocol_column, 
                owner_column=owner_column, 
                fingerprint_column=fingerprint_column)
            print(f"资产导入完成: 成功 {success_count} 个, 失败 {failed_count} 个")
        except Exception as e:
            print(f"导入资产时发生错误: {str(e)}")
    
    def export_assets(self, format_type, keyword=''):
        """导出资产到文件"""
        # 验证格式类型
        if format_type not in ['csv', 'json']:
            print(f"错误: 未知的导出格式 {format_type}！可用格式: csv, json")
            return
        
        # 构建导出SQL
        if keyword:
            sql = """SELECT a.*, o.owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id 
                      WHERE a.asset_name LIKE %s OR a.ip LIKE %s OR a.domain LIKE %s OR a.asset_tags LIKE %s 
                      ORDER BY a.create_time DESC"""
            keyword_pattern = f"%{keyword}%"
            assets_result = self.db_connector.execute_query(
                sql, (keyword_pattern, keyword_pattern, keyword_pattern, keyword_pattern)
            )
        else:
            sql = "SELECT a.*, o.owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id ORDER BY a.create_time DESC"
            assets_result = self.db_connector.execute_query(sql)
        
        if not assets_result:
            print(f"没有可导出的资产数据！")
            return
        
        # 转换为字典列表
        assets_data = []
        for row in assets_result:
            asset = Asset()
            asset.asset_id = row[0]
            asset.asset_name = row[1]
            asset.ip = row[2]
            asset.port = row[4]
            asset.domain = row[3]
            asset.asset_protocol = row[5]
            asset.asset_tags = row[6]
            asset.owner_id = row[7]
            asset.owner_name = row[10] if row[10] else '无'
            asset.create_time = datetime.strptime(row[8], '%Y-%m-%d %H:%M:%S') if row[8] else None
            asset.update_time = datetime.strptime(row[9], '%Y-%m-%d %H:%M:%S') if row[9] else None
            assets_data.append(asset.to_dict())
        
        # 执行导出
        export_path = FileExporter.export_data(assets_data, 'assets', format_type)
        print(f"资产导出成功！文件保存在: {export_path}")


    def scan_asset(self, identifier):
        """处理asset scan命令，检查单个资产的有效性"""
        # 解析identifier为ID还是URL
        assets = []
        
        # 如果是URL格式
        if identifier.startswith('http://') or identifier.startswith('https://'):
            from urllib.parse import urlparse
            parsed_url = urlparse(identifier)
            host = parsed_url.netloc.split(':')[0]  # 获取主机名
            port = parsed_url.port or (443 if parsed_url.scheme == 'https' else 80)  # 获取端口
            
            # 检查是否为IP地址
            try:
                ipaddress.ip_address(host)
                ip = host
                domain = None
            except ValueError:
                ip = None
                domain = host
                
            # 创建临时资产对象
            asset = {
                'asset_id': 'temp_url_asset',
                'ip': ip,
                'domain': domain,
                'port': port,
                'asset_protocol': 'tcp',
                'is_valid': False,
            }
            assets.append(asset)
        else:
            # 尝试作为资产ID或名称查询
            sql = "SELECT a.*, o.owner_name FROM assets a LEFT JOIN owners o ON a.owner_id = o.owner_id WHERE asset_id = %s OR asset_name = %s"
            cursor = self.db_connector.execute_query(sql, (identifier, identifier))
            
            if not cursor:
                print(f"未找到资产: {identifier}")
                return
            
            asset = cursor.fetchone()
            if not asset:
                print(f"未找到资产: {identifier}")
                return
            
            assets.append(asset)
        
        # 执行资产检查
        self._scan_assets(assets)


    def _scan_assets(self, assets, scan_filter='invalid'):
        """看端口是否开放，是否能获取到新的指纹信息等(指纹信息包括http/https/php/nginx等与服务、框架、系统有关的东西），如果是http页面，记录响应信息，从响应信息中是否能获取到title"""
        
        if scan_filter == 'invalid':
            assets = [asset for asset in assets if not asset['is_valid']]
        
        if not assets:
            print("没有要检查的资产")
            return
        
        # 创建服务扫描器实例
        scanner = ServiceScanner(timeout=3, retry_count=2)
        scanner.scan_assets(assets, threads=10)
        
        # print("开始扫描资产，每完成一个资产检测将立即显示结果...\n")
        
        # # 执行多线程扫描，每检测完一个就更新和打印一个
        # total_count, valid_count, open_count, update_count = scanner.scan_assets_one_by_one(assets, self.db_connector, threads=10)
        
        # # 显示总体扫描结果统计
        # print("\n======== 扫描结果统计 ========")
        # print(f"  总资产数: {total_count}")
        # print(f"  端口开放数: {open_count}")
        # print(f"  有效资产数: {valid_count}")
        # print(f"  数据库更新数: {update_count}")
        # print("==============================")
